using System;
using System.Collections.Generic;
using System.IO;
using FireStarter.Base;
using FireStarterModeller.BO;
using Habanero.Base;

namespace FireStarter.CodeGeneration
{
    /// <summary>
    /// Generates a Test.BO project for the solution
    /// </summary>
    public class BOTestProjectGenerator : ProjectGenerator
    {
        public const string TEST_UTILS_FOLDER = "TestUtils";

        public override Result Generate()
        {
            CheckRequiredPropertiesSet();
            IList<IModelClass> modelClasses = SolutionGenerator.ModelClasses;
            if (SolutionGenerator.ModelClasses == null) throw new PropertyMissingException("ModelClasses");
            Directory.CreateDirectory(ProjectPath);

            BoTestProjectTemplateLoader templateLoader = new BoTestProjectTemplateLoader(SolutionGenerator.ModelClasses);

            string contents = templateLoader.GetAssemblyInfo(ProjectName);
            string assemblyInfoFileName = Utilities.CreateFile(ProjectPath, "Properties\\AssemblyInfo.cs", contents);

            string projectFileName = Path.Combine(ProjectPath, ProjectName + ".csproj");
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, false, 
                Templates.csprojTemplate2008v3_5, SolutionGenerator.CodeTemplate);

            projectFileManager.AddProjectReference(ModelSolution.BOProjectName, SolutionGenerator.SolutionDirectory);
            projectFileManager.AddReference(Path.Combine(SolutionGenerator.HabaneroReferencePath, "Habanero.Base.dll"));
            projectFileManager.AddReference(Path.Combine(SolutionGenerator.HabaneroReferencePath, "Habanero.BO.dll"));
            projectFileManager.AddReference(Path.Combine(SolutionGenerator.HabaneroReferencePath, "Habanero.DB.dll"));
            projectFileManager.AddReference(Path.Combine(SolutionGenerator.HabaneroReferencePath, "Habanero.Testability.dll"));
            if (!projectFileManager.HasReference("nunit.framework.dll"))
                projectFileManager.AddReference(Path.Combine(SolutionGenerator.HabaneroReferencePath, "nunit.framework.dll"));

            CreateTestBaseClass(templateLoader, projectFileManager);
            CreateTestUtilsFolderAndClasses(templateLoader, projectFileManager, modelClasses);
            CreateTestBOClasses(templateLoader, projectFileManager, modelClasses);

            projectFileManager.SaveProjectFile();
            UpdateClassesNeedsRegenerationFlag();
            return new Result(true);
        }

        private void UpdateClassesNeedsRegenerationFlag()
        {
            foreach (IModelClass modelClass in SolutionGenerator.ModelClasses)
            {
                modelClass.NeedsRegeneration = false;
            }
        }

        private void CreateTestBaseClass(BoTestProjectTemplateLoader templateLoader, ProjectFileManager projectFileManager)
        {
            string contents = templateLoader.GetTestBaseClass(SolutionGenerator.ModelSolution);
            string fileName = Utilities.CreateFile(ProjectPath, "TestBase.cs", contents);
            projectFileManager.AddCodeFile(fileName);
        }

        private void CreateTestUtilsFolderAndClasses(BoTestProjectTemplateLoader templateLoader, ProjectFileManager projectFileManager, IList<IModelClass> modelClasses)
        {
            string boProjectName = SolutionGenerator.ModelSolution.BOProjectName;
            string contents = templateLoader.GetTestUtilsClass(SolutionGenerator.ModelSolution);
            string path = ProjectPath + Path.DirectorySeparatorChar + TEST_UTILS_FOLDER;
            projectFileManager.AddFolder(path);

            string fileName = Utilities.CreateFile(path, "TestUtilsShared.cs", contents);
            projectFileManager.AddCodeFile(fileName);

            foreach (IModelClass modelClass in modelClasses)
            {
                if (!modelClass.GenerateCode) continue;
                contents = templateLoader.GetBoTestClass_TestUtilsBO(ProjectName, boProjectName, modelClass);
                string mainClassFileName = String.Format("TestUtils{0}.cs", modelClass.ClassName);
                string mainFilePath = Utilities.CreateFile(path, mainClassFileName, contents, false);
                projectFileManager.AddCodeFile(mainFilePath);

                contents = templateLoader.GetBoTestClass_TestUtilsBODef(ProjectName, boProjectName, modelClass);
                string defClassFileName = String.Format("TestUtils{0}.Def.cs", modelClass.ClassName);
                string defFilePath = Utilities.CreateFile(path, defClassFileName, contents, true);
                projectFileManager.AddCodeFile(defFilePath, mainFilePath);
            }
        }

        private void CreateTestBOClasses(BoTestProjectTemplateLoader templateLoader, ProjectFileManager projectFileManager, IList<IModelClass> modelClasses)
        {
            string boProjectName = SolutionGenerator.ModelSolution.BOProjectName;
                
            foreach (IModelClass modelClass in modelClasses)
            {
                if (!modelClass.GenerateCode) continue;
                string contents = templateLoader.GetBoTestClass_Main(ProjectName, boProjectName, modelClass);
                string mainClassFileName = String.Format("Test{0}.cs", modelClass.ClassName);
                string mainFilePath = Utilities.CreateFile(ProjectPath, mainClassFileName, contents, false);
                projectFileManager.AddCodeFile(mainFilePath);

                string fileName = modelClass.ClassName;
                fileName = Path.Combine(ProjectPath, fileName);
                if ((modelClass.NeedsRegeneration) || ((!File.Exists(fileName))))
                {
                    contents = templateLoader.GetBoTestClass_Generated(ProjectName, boProjectName, modelClass);
                    string defClassFileName = String.Format("Test{0}.Def.cs", modelClass.ClassName);
                    string defFilePath = Utilities.CreateFile(ProjectPath, defClassFileName, contents, true);
                    projectFileManager.AddCodeFile(defFilePath, mainFilePath);
                }
            }
        }
    }
}


// Keeping this on hand as a potential alternate approach in the future

//using System;
//using System.Collections.Generic;
//using System.IO;
//using FireStarterModeller.BO;
//using Habanero.Base;

//namespace FireStarter.CodeGeneration
//{
//    public class BoTestProjectGenerator: IGenerator_Brett, IProjectGenerator
//    {
//        public const string TEST_UTILS_FOLDER_NAME = "TestUtils";

//        public BoTestProjectGenerator(string projectName, string boProjectFileName, 
//                string habaneroReferencePath, string projectPath, ISolutionGenerator solutionGenerator)
//        {
//            GeneratedClasses = new Dictionary<string, ClassGenerator>(4);
//            References = new Dictionary<string, Reference>(6);
//            if (string.IsNullOrEmpty(projectName)) throw new ArgumentNullException("projectName");
//            if (string.IsNullOrEmpty(boProjectFileName )) throw new ArgumentNullException("boProjectFileName");
//            if (string.IsNullOrEmpty(habaneroReferencePath )) throw new ArgumentNullException("habaneroReferencePath");
//            if (string.IsNullOrEmpty(projectPath)) throw new ArgumentNullException("projectPath");

//            ProjectName = projectName;
//            ProjectPath = projectPath;
//            HabaneroReferencePath = habaneroReferencePath;
//            BoProjectFileName = boProjectFileName;
//            SolutionGenerator = solutionGenerator;
//        }

//        public BoTestProjectGenerator()
//        {
//            GeneratedClasses = new Dictionary<string, ClassGenerator>(4);
//            References = new Dictionary<string, Reference>(6);
//        }

//        public string ProjectName { get; set; }
//        public string BoProjectFileName { get; set; }
//        public string LogicProjectFileName { get; set; }
//        public string UIBaseProjectFileName { get; set; }
//        public string UIVWGProjectFileName { get; set; }
//        public string UIWinProjectFileName { get; set; }
//        public string HabaneroReferencePath { get; set; }
//        public string ProjectPath { get; set; }
//        public ISolutionGenerator SolutionGenerator { get; set; }
//        public Dictionary<string, Reference> References { get; private set; }
//        public Dictionary<string, ClassGenerator> GeneratedClasses { get; private set; }
//        public string AssemblyInfoFileContents { get; private set; }
//        public ProjectFileManager ProjectFileManagerBoTest { get; private set; }

//        public string ProjectFileName
//        {
//            get { return Path.Combine(ProjectPath, ProjectName + ".csproj"); }
//        }

//        private string BoProjectName
//        {
//            get { return SolutionGenerator.ModelSolution.BOProjectName; }
//        }

//        public void Construct()
//        {
//            AddReference("Habanero.Base.dll");
//            AddReference("Habanero.BO.dll");
//            AddReference("Habanero.DB.dll");
//            AddReference("nunit.framework.dll");

//            GeneratedClasses.Add("TestBase", new ClassGeneratorTestBase("TestBase", ProjectName, ProjectPath));
//            ProjectFileManager boProjectFileManager = new ProjectFileManager(BoProjectFileName, SolutionGenerator.CodeTemplate);
//            ClassGeneratorTestUtils generatorTestUtils = new ClassGeneratorTestUtils("TestUtils", ProjectName, 
//                    ProjectPath, boProjectFileManager.AssemblyName, SolutionGenerator.ModelClasses);
//            GeneratedClasses.Add("TestUtils", generatorTestUtils);
//            foreach (IModelClass modelClass in SolutionGenerator.ModelClasses)
//            {
//                GeneratedClasses.Add(modelClass.ClassName, new ClassGeneratorTestBo(modelClass.ClassName, ProjectName, ProjectPath));
//            }
//            ProjectFileManagerBoTest = new ProjectFileManager(ProjectFileName, OutputType.Library, SolutionGenerator.CodeTemplate);
//        }

//        private void AddReference(string referenceDllName)
//        {
//            References.Add(referenceDllName, new Reference(HabaneroReferencePath, referenceDllName));
//        }
//        /// <summary>
//        /// Loads all relevant templates for this class and all of its dependent classes.
//        ///   Parses the these templates and replaces all tags in the template with appropriate values
//        ///   thus creating output file contents.
//        /// </summary>
//        public void Compile()
//        {
//            //BoTestProjectTemplateLoader boTestProjectTemplateLoader = new BoTestProjectTemplateLoader();
//            // Sets up the AssemblyInfo content from the template
//            TemplateLoader templateLoader = new TemplateLoader();
//            AssemblyInfoFileContents = templateLoader.GetAssemblyInfo(ProjectName);
//            // Adds all the necessary references to the ProjectFileManager
//            foreach (Reference reference in References.Values)
//            {
//                ProjectFileManagerBoTest.AddReference(reference.FullReferencePath);
//            }
//            // Adds all the necessary files to the ProjectFileManager
//            //  AssemblyInfo, and all Code Files
//            string assemblyInfoFileName = Path.Combine(ProjectPath, @"Properties\AssemblyInfo.cs");
//            ProjectFileManagerBoTest.AddCodeFile(assemblyInfoFileName);
//            foreach (ClassGenerator classGenerator in GeneratedClasses.Values)
//            {
//                string fileName = Path.Combine(ProjectPath, classGenerator.FileName);
//                ProjectFileManagerBoTest.AddCodeFile(fileName);
//            }
//            //Runs the Compile method on all the Class Generators
//            foreach (ClassGenerator classGenerator in GeneratedClasses.Values)
//            {
//                classGenerator.Compile();
//            }
//        }

//        public Result Generate()
//        {
//            string projectFileName = Path.Combine(ProjectPath, ProjectName + ".csproj");

//            Directory.CreateDirectory(ProjectPath);

//            BoTestProjectTemplateLoader boTestProjectTemplateLoader = new BoTestProjectTemplateLoader(SolutionGenerator.ModelClasses);

//            CreateAssemblyInfoFile(boTestProjectTemplateLoader);

//            ProjectFileManagerBoTest = new ProjectFileManager(projectFileName, OutputType.Library, SolutionGenerator.CodeTemplate);
//            CreateTestBaseClass(boTestProjectTemplateLoader);
//            CreateTestUtilsClass(boTestProjectTemplateLoader);
//            ProjectFileManagerBoTest.AddProjectReference(BoProjectFileName);

//            ProjectFileManagerBoTest.AddFolder("TestUtils");
//            CreateBoClassFiles(boTestProjectTemplateLoader);

//            AddReferences();
//            ProjectFileManagerBoTest.SaveProjectFile();
//            return new Result(true);
//        }

//        private void CreateAssemblyInfoFile(TemplateLoader boTestProjectTemplateLoader)
//        {
//            string contents = boTestProjectTemplateLoader.GetAssemblyInfo(ProjectName);
//            Utilities.CreateFile(ProjectPath, "Properties\\AssemblyInfo.cs", contents);
//        }

//        private void AddReferences()
//        {
//            ProjectFileManagerBoTest.AddReference(Path.Combine(HabaneroReferencePath, "Habanero.Base.dll"));
//            ProjectFileManagerBoTest.AddReference(Path.Combine(HabaneroReferencePath, "Habanero.BO.dll"));
//            ProjectFileManagerBoTest.AddReference(Path.Combine(HabaneroReferencePath, "Habanero.DB.dll"));
//            //ProjectFileManagerBoTest.AddReference(Path.Combine(HabaneroReferencePath, "Habanero.Util.dll"));
//            ProjectFileManagerBoTest.AddReference(Path.Combine(HabaneroReferencePath, "nunit.framework.dll"));
//            //ProjectFileManagerBoTest.AddReference(BoProjectFileName);
//        }

////        private void ValidateProjectSetup()
////        {
////            if (BoProjectFileName == null) throw new ReferenceMissingException("BO Project");
////            if (ProjectName == null) throw new PropertyMissingException("ProjectName");
////            if (ProjectPath == null) throw new PropertyMissingException("ProjectPath");
////            if (ModelElements == null) throw new PropertyMissingException("ModelElements");
////            if (HabaneroReferencePath == null) throw new PropertyMissingException("HabaneroReferencePath");
////        }

//        private void CreateTestBaseClass(BoTestProjectTemplateLoader boTestProjectTemplateLoader)
//        {
//            string contents = boTestProjectTemplateLoader.GetTestBaseClass(ProjectName, BoProjectName);
//            string uiManagerFileName = Utilities.CreateFile(ProjectPath, "TestBase.cs", contents);
//            ProjectFileManagerBoTest.AddCodeFile(uiManagerFileName);
//        }

//        private void CreateTestUtilsClass(BoTestProjectTemplateLoader boTestProjectTemplateLoader)
//        {
//            string testUtilsPath = Path.Combine(ProjectPath, TEST_UTILS_FOLDER_NAME);

//            string contents = boTestProjectTemplateLoader.GetTestUtilsClass(ProjectName, BoProjectName, SolutionGenerator.ModelClasses);
//            string uiManagerFileName = Utilities.CreateFile(testUtilsPath, "TestUtilsShared.cs", contents, false);
//            ProjectFileManagerBoTest.AddCodeFile(uiManagerFileName);
//        }

//        private void CreateBoClassFiles(BoTestProjectTemplateLoader boTestProjectTemplateLoader)
//        {
//            foreach (IModelClass modelClass in SolutionGenerator.ModelClasses)
//            {
//                string contents = boTestProjectTemplateLoader.GetBoTestClass_Main(ProjectName, BoProjectName, modelClass);
//                string boTestClassMain = Utilities.CreateFile(ProjectPath, "Test" + modelClass.ClassName + ".cs", contents, false);
//                ProjectFileManagerBoTest.AddCodeFile(boTestClassMain);

//                contents = boTestProjectTemplateLoader.GetBoTestClass_Generated(ProjectName, BoProjectName, modelClass);
//                string boTestClassGenerated = Utilities.CreateFile(ProjectPath, "Test" + modelClass.ClassName + ".Def.cs", contents, true);
//                ProjectFileManagerBoTest.AddCodeFile(boTestClassGenerated, boTestClassMain);

//                string testUtilsPath = Path.Combine(ProjectPath, TEST_UTILS_FOLDER_NAME);

//                contents = boTestProjectTemplateLoader.GetBoTestClass_TestUtilsBO(ProjectName, BoProjectName, modelClass);
//                string boTestClassTestUtilsBO = Utilities.CreateFile(testUtilsPath, TEST_UTILS_FOLDER_NAME + modelClass.ClassName + ".cs", contents, false);
//                ProjectFileManagerBoTest.AddCodeFile(boTestClassTestUtilsBO);

//                contents = boTestProjectTemplateLoader.GetBoTestClass_TestUtilsBODef(ProjectName, BoProjectName, modelClass);
//                string boTestClassTestUtilsBODef = Utilities.CreateFile(testUtilsPath, TEST_UTILS_FOLDER_NAME + modelClass.ClassName + ".Def.cs", contents, true);
//                string dependantUponFile = boTestClassTestUtilsBO.Replace(TEST_UTILS_FOLDER_NAME + Path.DirectorySeparatorChar, "");
//                ProjectFileManagerBoTest.AddCodeFile(boTestClassTestUtilsBODef, dependantUponFile);
//            }
//        }
//    }

//    public class Reference
//    {
//        public Reference(string referencePath, string referenceDllName)
//        {
//            ReferencePath = referencePath;
//            ReferenceDllName = referenceDllName;
//        }

//        public string ReferencePath { get; private set; }

//        public string ReferenceDllName { get; private set; }

//        public string FullReferencePath
//        {
//            get { return Path.Combine(ReferencePath, ReferenceDllName); }
//        }
//    }
//}
